package com.ajavaer.framework.pay;

import com.ajavaer.framework.common.message.Message;
import com.ajavaer.framework.common.tools.JsonTools;
import com.ajavaer.framework.common.tools.ObjectTools;
import com.ajavaer.framework.common.tools.ServletTools;
import com.ajavaer.framework.common.tools.StringTools;
import com.ajavaer.framework.config.ApplePayConfig;
import com.ajavaer.framework.handle.ApplePayHandle;
import com.ajavaer.framework.pay.response.AppleVerifyResponse;
import com.ajavaer.framework.pay.response.item.AppleVerifyReceiptInApp;
import com.ajavaer.framework.pay.response.item.AppleVerifyReceiptV1;
import com.ajavaer.framework.pay.response.item.AppleVerifyReceiptV2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

/**
 * applePay凭据验证
 * <a href='https://www.processon.com/view/link/598c062be4b02e9a26eed69a'>时序图</a>
 */
@Component
public class ApplePay implements InitializingBean {
    private Logger log = LoggerFactory.getLogger(ApplePay.class);
    private TrustManager myX509TrustManager;
    @Resource
    private ApplePayConfig applePayConfig;
    @Resource
    private Environment environment;
    private Boolean isDebug = true;


    /**
     * applePay
     * <a href='https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1'>文档</a>
     *
     * @param request
     * @param response
     * @param applePayHandle
     * @throws Exception
     */
    public <T> void payCallBack(HttpServletRequest request, HttpServletResponse response, ApplePayHandle<T> applePayHandle) throws Exception {
        Map<String, Object> params = JsonTools.jsonToMap(ServletTools.getRequestBody(request), false);
        if (params == null) {
            ServletTools.printObjJson(response, Message.fail("request body is null"));
            return;
        }
        if (isDebug) {
            log.debug("[Pay Apple] payCallBack params:" + JsonTools.beanToJson(params));
        }
        applePayHandle.onMessage(params);
        String receipt = (String) params.get("receipt");
        Boolean chooseEnv;
        if(params.get("chooseEnv") == null || params.get("chooseEnv").toString().equals("true") || params.get("chooseEnv").toString().equals("1")){
            chooseEnv = true;
        }else{
            chooseEnv = false;
        }
        if (!applePayConfig.getEnableSandbox() && !chooseEnv) {
            ServletTools.printObjJson(response, Message.fail("not allowed to use sandbox"));
            return;
        }
        if (StringTools.isBlank(receipt)) {
            ServletTools.printObjJson(response, Message.fail("receipt must not be null"));
            return;
        }
        //苹果返回的string
        String appleResponseString = verify(receipt, chooseEnv).toString();
        AppleVerifyResponse appleResponse = JsonTools.jsonToBean(appleResponseString, AppleVerifyResponse.class);
        if (appleResponse == null) {
            ServletTools.printObjJson(response, Message.fail("凭据信息不正确"));
            log.error("[apple response] " + appleResponseString);
            return;
        } else {
            //如果是生产环境,先看生产,在看沙箱
            if (chooseEnv && appleResponse.getStatus() == 21007) {
                appleResponse = JsonTools.jsonToBean(verify(receipt, false).toString(), AppleVerifyResponse.class);
                if (appleResponse == null) {
                    ServletTools.printObjJson(response, Message.fail("凭据信息不正确"));
                    log.error("[apple response] " + appleResponseString);
                    return;
                }
            }
            Integer status = appleResponse.getStatus();
            switch (status) {
                case 0:
                    Map<String, Object> receiptResponse = appleResponse.getReceipt();
                    AppleVerifyReceiptInApp appleVerifyReceiptInApp = null;
                    if (receiptResponse.get("in_app") != null) {
                        AppleVerifyReceiptV2 receiptV2 = JsonTools.jsonToBean(JsonTools.beanToJson(receiptResponse), AppleVerifyReceiptV2.class);
                        if (receiptV2 == null) {
                            ServletTools.printObjJson(response, Message.fail("凭据信息不正确"));
                            log.error("[apple response] " + appleResponseString);
                            return;
                        }
                        String receipt_creation_date_ms = receiptV2.getReceipt_creation_date_ms();// receipt创建日期(ms)
                        for (AppleVerifyReceiptInApp item : receiptV2.getIn_app()) {
                            //找到本次的单子
                            if (item.getPurchase_date_ms().equals(receipt_creation_date_ms)) {
                                appleVerifyReceiptInApp = item;
                                break;
                            }
                        }
                        if (appleVerifyReceiptInApp == null) {
                            ServletTools.printObjJson(response, Message.fail("凭据信息不正确"));
                            log.error("[apple response] " + appleResponseString);
                            return;
                        }
                    } else {
                        AppleVerifyReceiptV1 receiptV1 = JsonTools.jsonToBean(JsonTools.beanToJson(receiptResponse), AppleVerifyReceiptV1.class);
                        if (receiptV1 == null) {
                            ServletTools.printObjJson(response, Message.fail("凭据信息不正确"));
                            log.error("[apple response] " + appleResponseString);
                            return;
                        }
                        appleVerifyReceiptInApp = new AppleVerifyReceiptInApp();
                        BeanUtils.copyProperties(receiptV1, appleVerifyReceiptInApp);
                    }
                    T order = applePayHandle.getByOutTradeNo(params);
                    if (order == null) {
                        ServletTools.printObjJson(response, Message.fail("订单信息不存在"));
                        log.error("[apple response] " + appleResponseString);
                        return;
                    }
                    //获取订单,并判断是否处理过
                    Boolean checkTransactionId = applePayHandle.checkOrder(order, appleVerifyReceiptInApp);
                    if (!checkTransactionId) {
                        ServletTools.printObjJson(response, Message.success());
                        log.info("[apple response] repeat " + appleResponseString);
                        return;
                    }
                    applePayHandle.business(order, appleVerifyReceiptInApp);
                    ServletTools.printObjJson(response, Message.success());
                    break;
                case 21007:
                    ServletTools.printObjJson(response, Message.fail("收据信息是测试用（sandbox），但却被发送到正式环境中验证"));
                    log.error("[apple response] " + appleResponseString);
                    return;
                case 21008:
                    ServletTools.printObjJson(response, Message.fail("收据信息是生产用，但却发送至Sandbox环境的验证服务"));
                    log.error("[apple response] " + appleResponseString);
                    return;
                default:
                    ServletTools.printObjJson(response, Message.fail("applePay response status:" + status));
                    log.error("[apple response] " + appleResponseString);
                    return;
            }
        }
    }

    /**
     * 请求apple服务验证收据
     *
     * @param receipt
     * @param chooseEnv
     * @return
     * @throws Exception
     */
    private StringBuilder verify(String receipt, Boolean chooseEnv) throws Exception {
        String verifyReceiptUrl = chooseEnv ? applePayConfig.getVerifyReceiptUrl() : applePayConfig.getVerifyReceiptUrlSandbox();
        // 设置SSLContext
        SSLContext ssl = SSLContext.getInstance("SSL");
        ssl.init(null, new TrustManager[]{myX509TrustManager}, null);
        // 打开连接
        HttpsURLConnection conn = (HttpsURLConnection) new URL(verifyReceiptUrl).openConnection();
        // 设置套接工厂
        conn.setSSLSocketFactory(ssl.getSocketFactory());
        // 加入数据
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-type", "application/json");
        conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
        conn.setDoOutput(true);
        Map<String, Object> receiptData = new HashMap<>();
        receiptData.put("receipt-data", receipt);
        BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
        buffOutStr.write(JsonTools.beanToJson(receiptData).getBytes());
        buffOutStr.flush();
        buffOutStr.close();
        // 获取输入流
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;
        StringBuilder sb = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        conn.getInputStream().close();
        return sb;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        /**
         * 重写X509TrustManager
         */
        myX509TrustManager = new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
        };

        this.isDebug = this.environment.getProperty("ajavaer.debug", Boolean.class, true);
        if (isDebug) {
            log.debug("Pay Apple init");
        }
    }
}
